package com.demo.service.riot;

import com.demo.common.HttpErrorType;
import com.demo.config.RiotConfig;
import com.demo.exception.ApiException;
import com.demo.exception.RateLimitExceededException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import org.springframework.stereotype.Service;

import javax.annotation.PreDestroy;
import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Collectors;

import com.demo.common.ErrorDetail;
import com.demo.exception.ApiException;

@Service
@Slf4j
public class RiotBasicService implements RiotService {
    private final OkHttpClient client;
    private final ObjectMapper objectMapper;
    private final RiotConfig config;

    private final ConcurrentHashMap<String, RegionRateLimiter> regionLimiters = new ConcurrentHashMap<>();
    private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
    private final AtomicLong totalRequests = new AtomicLong(0);
    private final AtomicLong blockedRequests = new AtomicLong(0);
    private static final String GLOBAL_REGION = "global";

    public RiotBasicService(OkHttpClient client, RiotConfig config) {
        this.client = client;
        this.objectMapper = new ObjectMapper();
        this.config = config;

        // 初始化速率监控任务
        initRateMonitoring();
    }

    @PreDestroy
    public void destroy() {
        scheduler.shutdown();
        try {
            if (!scheduler.awaitTermination(5, TimeUnit.SECONDS)) {
                scheduler.shutdownNow();
            }
        } catch (InterruptedException e) {
            scheduler.shutdownNow();
            Thread.currentThread().interrupt();
        }
    }

    private void initRateMonitoring() {
        // 每5秒打印一次监控数据
        scheduler.scheduleAtFixedRate(() -> {
            log.info("API 监控统计 - 总请求: {}, 已阻止: {}, 当前区域限流器: {}",
                    totalRequests.get(), blockedRequests.get(), regionLimiters.size());

            regionLimiters.forEach((region, limiter) -> {
                Map<String, Integer> metrics = limiter.getMetrics();
                log.debug("区域 {} 监控: 秒级可用许可={}, 分钟计数={}, 总请求={}, 已阻止={}",
                        region,
                        metrics.get("availablePermits"),
                        metrics.get("minuteCount"),
                        metrics.get("totalRequests"),
                        metrics.get("blockedRequests"));
            });
        }, 5, 5, TimeUnit.SECONDS);

        // 每10分钟清理不活跃的限流器
        scheduler.scheduleAtFixedRate(() -> {
            long now = System.currentTimeMillis();
            regionLimiters.entrySet().removeIf(entry ->
                    now - entry.getValue().getLastAccessTime() > 600_000
            );
            log.debug("清理后区域限流器数量: {}", regionLimiters.size());
        }, 10, 10, TimeUnit.MINUTES);

        // 每秒补充秒级令牌
        scheduler.scheduleAtFixedRate(() -> {
            regionLimiters.values().forEach(RegionRateLimiter::refillSecondBucket);
        }, 1, 1, TimeUnit.SECONDS);

        // 每分钟重置分钟计数器
        scheduler.scheduleAtFixedRate(() -> {
            regionLimiters.values().forEach(RegionRateLimiter::resetMinuteCounter);
        }, 1, 1, TimeUnit.MINUTES);
    }

    private static class RegionRateLimiter {
        private final Semaphore secondBucket = new Semaphore(20); // 每秒20个请求
        private final AtomicInteger minuteCounter = new AtomicInteger(0);
        private final AtomicInteger totalRequests = new AtomicInteger(0);
        private final AtomicInteger blockedRequests = new AtomicInteger(0);
        private volatile long lastAccessTime = System.currentTimeMillis();
        public boolean tryAcquire() {
            lastAccessTime = System.currentTimeMillis();
            totalRequests.incrementAndGet();

            // 检查分钟限制
            if (minuteCounter.incrementAndGet() > 100) {
                minuteCounter.decrementAndGet();
                blockedRequests.incrementAndGet();
                return false;
            }

            // 检查秒级限制
            if (secondBucket.tryAcquire()) {
                return true;
            } else {
                blockedRequests.incrementAndGet();
                minuteCounter.decrementAndGet(); // 回滚分钟计数
                return false;
            }
        }

        public void refillSecondBucket() {
            int available = secondBucket.availablePermits();
            if (available < 20) {
                secondBucket.release(20 - available);
            }
        }

        public void resetMinuteCounter() {
            minuteCounter.set(0);
        }

        public Map<String, Integer> getMetrics() {
            return Map.of(
                    "availablePermits", secondBucket.availablePermits(),
                    "minuteCount", minuteCounter.get(),
                    "totalRequests", totalRequests.get(),
                    "blockedRequests", blockedRequests.get()
            );
        }

        public long getLastAccessTime() {
            return lastAccessTime;
        }
    }

    /**
     * 通用API调用方法（带速率限制）
     *
     * @param region 区域代码（如 na1, euw1, kr）
     * @param endpoint 端点路径
     * @param pathParams 路径参数
     * @param queryParams 查询参数
     */
    private JsonNode callApi(String region,
                            String endpoint,
                            Map<String, String> pathParams,
                            Map<String, String> queryParams) throws ApiException {
        // 1. 处理区域参数（可为空）
        String effectiveRegion = (region == null || region.isBlank()) ?
                GLOBAL_REGION : region.toLowerCase();

        // 2. 获取区域限流器
        RegionRateLimiter limiter = regionLimiters.computeIfAbsent(effectiveRegion, r -> {
            log.info("为区域 {} 创建新的速率限制器", effectiveRegion);
            return new RegionRateLimiter();
        });

        // 3. 应用速率限制
        totalRequests.incrementAndGet();
        if (!limiter.tryAcquire()) {
            blockedRequests.incrementAndGet();
            throw new RateLimitExceededException(
                    "API速率限制超出: 区域=" + effectiveRegion +
                            " 当前秒级许可=" + limiter.secondBucket.availablePermits() +
                            " 分钟计数=" + limiter.minuteCounter.get()
            );
        }

        // 4. 执行API调用
        int code = 500;
        String msg = "";
        try {
            // 构建完整URL
            String url = buildUrl(endpoint, pathParams, queryParams);

            // 创建请求
            Request request = new Request.Builder()
                    .url(url)
                    .addHeader("X-Riot-Token", config.getKey())
                    .build();
            log.debug("Calling Riot API: {}", url);
            // 执行请求
            try (Response response = client.newCall(request).execute()) {
                log.debug("Response code: {}", response.code());
                code = response.code();
                msg = response.message();
                // 处理响应
                return processResponse(response);
            }
        } catch (Exception e) {
            log.error("Riot API调用失败", e); // 先记录日志
            throw new ApiException(
                    ErrorDetail.builder()
                            .httpCode(code)
                            .message("Riot API 调用失败")
                            .reason(HttpErrorType.findByStatusCode(code).getDescription())
                            .build()  // 先 build() 生成对象
                            .addMetadata("region", region)  // 再调用 addMetadata
            );
        }
    }

    /**
     * 通用的Riot API调用方法（自动处理区域URL设置）
     *
     * @param region 区域代码
     * @param endpoint API端点
     * @param pathParams 路径参数
     * @param queryParams 查询参数
     */
    private JsonNode callApiWithRegion(RiotConfig riotConfig,
                                       String region,
                                       String endpoint,
                                       Map<String, String> pathParams,
                                       Map<String, String> queryParams) throws ApiException {

        // 保存原始基础URL
        String originalBaseUrl = riotConfig.getBaseurl();

        try {
            // 设置区域特定的基础URL
            String regionBaseUrl = "https://" + region + ".api.riotgames.com";
            riotConfig.setBaseurl(regionBaseUrl);

            // 调用基础API并返回原始JSON结果
            return callApi(region, endpoint, pathParams, queryParams);
        } finally {
            // 确保恢复原始基础URL
            riotConfig.setBaseurl(originalBaseUrl);
        }
    }

    private String encodePathParam(String value) {
        return RiotApiEncoder.encodeForRiotApi(value);
    }

    private String encodeQueryParam(String value) {
        try {
            return URLEncoder.encode(value, StandardCharsets.UTF_8.toString());
        } catch (Exception e) {
            return value;
        }
    }

    /**
     *
     * @param endpoint 端点路径
     * @param pathParams 路径参数映射
     * @param queryParams 查询参数映射
     * @return 用于访问的url
     */
    private String buildUrl(String endpoint,
                           Map<String, String> pathParams,
                           Map<String, String> queryParams)  {
        // 1. 替换路径参数
        String resolvedEndpoint = endpoint;
        if (pathParams != null) {
            for (Map.Entry<String, String> entry : pathParams.entrySet()) {
                // 使用专门的路径参数编码
                String encodedValue = encodePathParam(entry.getValue());
                resolvedEndpoint = resolvedEndpoint.replace(
                        "{" + entry.getKey() + "}",
                        encodedValue
                );
            }
        }
        // 2. 添加查询参数
        StringBuilder urlBuilder = new StringBuilder(config.getBaseurl())
                .append(resolvedEndpoint);
        if (queryParams != null && !queryParams.isEmpty()) {
            urlBuilder.append("?");
            for (Map.Entry<String, String> entry : queryParams.entrySet()) {
                // 对查询参数使用标准编码
                String encodedKey = encodeQueryParam(entry.getKey());
                String encodedValue = encodeQueryParam(entry.getValue());

                urlBuilder.append(encodedKey)
                        .append("=")
                        .append(encodedValue)
                        .append("&");
            }
            // 删除最后一个多余的"&"
            urlBuilder.deleteCharAt(urlBuilder.length() - 1);
        }
        return urlBuilder.toString();
    }

    @Override
    public JsonNode getJsonByParams(RiotConfig riotConfig,
                                    Map<String, String> pathParams,
                                    Map<String, String> queryParams,
                                    String serverRegion,
                                    String endpoint,
                                    String type,
                                    boolean useServerRegion) throws ApiException {
        log.debug("获取"+ type +"信息中");
        String region = RegionTransfer.getRegion(serverRegion);
        try {
            // 使用通用方法调用API
            JsonNode result = callApiWithRegion(
                    riotConfig,
                    useServerRegion ? serverRegion : region,
                    endpoint,
                    pathParams,
                    null
            );

            log.info("成功获取" + type + "信息");
            return result;
        } catch (ApiException e) {
            log.error("获取" + type + "信息失败，原因: {}", e.getMessage());
            throw e;
        }
    }

    /**
     * 处理API响应
     */
    private JsonNode processResponse(Response response) throws IOException {
        if (!response.isSuccessful()) {
            String errorBody = response.body() != null ?
                    response.body().string() : "Empty response body";
            log.error("API error: status={}, body={}", response.code(), errorBody);
            throw new IOException("Riot API错误: " + response.code());
        }

        return objectMapper.readTree(response.body().byteStream());
    }

    /**
     * 获取当前监控数据（用于API端点或日志）
     */
    public Map<String, Object> getRateLimitMetrics() {
        return Map.of(
                "totalRequests", totalRequests.get(),
                "blockedRequests", blockedRequests.get(),
                "activeRegions", regionLimiters.size(),
                "perRegionMetrics", regionLimiters.entrySet().stream()
                        .collect(Collectors.toMap(
                                Map.Entry::getKey,
                                e -> e.getValue().getMetrics()
                        ))
        );
    }

}
